﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Text;
using System.Windows.Forms;

namespace NetworkScanner
{
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="e"></param>
    public delegate void NetworkEventHandler(object sender, NetworkEventArgs e);

    public partial class FormPrincipal : Form
    {

        Network[] Networks;
        IPAddress IpDesde = new IPAddress(0);
        IPAddress IpHasta = new IPAddress(0);
        uint Cantidad = 0;

        public FormPrincipal()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            ActualizarInterfaces();
            progressBar1.Visible = false;
            textBoxDesde.Enabled = radioButtonPersonalizar.Checked;
            textBoxHasta.Enabled = radioButtonPersonalizar.Checked;
#if DEBUG
            textBoxIP.Text = "google.com";
            this.Text += " DEBUG";
            radioButtonPersonalizar.Select();
            textBoxDesde.Text = "192.168.1.10";
            textBoxHasta.Text = textBoxDesde.Text;
#endif
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Ping pingSender = new Ping();
            try
            {
                Log("Ping enviado a " + textBoxIP.Text);
                label3.Text = "ENVIANDO PING...              ";
                label3.Refresh();
                PingReply reply = pingSender.Send(textBoxIP.Text, 1000);
                switch (reply.Status)
                {
                    case IPStatus.Success:
                        label3.Text = "RESPUESTA EXITOSA";
                        Log("Respuesta desde [" + reply.Address.ToString() + "] en " + reply.RoundtripTime + " ms ( " + textBoxIP.Text + " )");
                        break;
                    case IPStatus.TimedOut:
                        label3.Text = "TIEMPO DE ESPERA AGOTADO";
                        break;
                    default:
                        {
                            label3.Text = "NO HAY RESPUESTA (ver log)";
                            Log(" Ping error: " + reply.Status.ToString());
                            break;
                        }
                }
            }
            catch (Exception)
            {
                label3.Text = "ERROR: HOST NO ENCONTRADO";
                Log("ERROR: Host no encontrado.");
            }
        }

        private void salirToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Application.Exit();
        }

        private void button_Discover(object sender, EventArgs e)
        {
            listViewHosts.Items.Clear();
            listViewHosts.Refresh();
            progressBar1.Value = 0;
            progressBar1.Visible = true;
            buttonDiscover.Enabled = false;
            Log("Comienza busqueda de hosts...");
            if (Cantidad > 2048)
            {
                DialogResult result = MessageBox.Show("Esta seguro que quiere recorrer " + Cantidad + " IPs,\npodria causar inestabilidad del sistema.",
                                                        "Advertencia", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);
                if (result == System.Windows.Forms.DialogResult.Yes)
                {
                    Networks[comboBoxNICS.SelectedIndex].EscanearRango(IpDesde, Cantidad);
                }
                else
                {
                    HostsChangedAsync(this, new NetworkEventArgs(NetworkEventArgs.TipoEvento.BusquedaCompletada, 0));
                }
            }
            else
            {
                Networks[comboBoxNICS.SelectedIndex].EscanearRango(IpDesde, Cantidad);
            }
        }
        /// <summary>
        /// Se ejecuta al seleccionar una interfaz de red desde el Combobox
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void comboBoxNICS_SelectedIndexChanged(object sender, EventArgs e)
        {
            labelIP.Text = Networks[comboBoxNICS.SelectedIndex].Addr.ToString();
            labelSubnetIP.Text = Networks[comboBoxNICS.SelectedIndex].SubnetAddr.ToString();
            labelSubnetIP.Text += " / " + Networks[comboBoxNICS.SelectedIndex].SubnetMask.PrefixLength();
            labelMask.Text = Networks[comboBoxNICS.SelectedIndex].SubnetMask.ToString();
            IpDesde = Networks[comboBoxNICS.SelectedIndex].SubnetAddr.Incrementar(1);
            IpHasta = Networks[comboBoxNICS.SelectedIndex].SubnetAddr.Incrementar(Networks[comboBoxNICS.SelectedIndex].maxHosts);
            textBoxDesde.Text = IpDesde.ToString();
            textBoxHasta.Text = IpHasta.ToString();
            RefreshHostsListAsync();
        }

        /// <summary>
        /// Delegado para realizar llamada a HostsChangedAsync desde otro proceso
        /// </summary>
        delegate void HostChangedCallback(object sender, NetworkEventArgs e);

        /// <summary>
        /// Oyente para notificar el cambio en la coleccion de hosts.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        public void HostsChangedAsync(object sender, NetworkEventArgs e)
        {
            if (progressBar1.InvokeRequired)
            {
                HostChangedCallback d = new HostChangedCallback(HostsChangedAsync);
                Invoke(d, sender, e);
            }
            else
            {
                switch (e.Tipo)
                {
                    case NetworkEventArgs.TipoEvento.HostEncontrado:
                        RefreshHostsListAsync();
                        progressBar1.Value = e.Porcentaje;
                        break;
                    case NetworkEventArgs.TipoEvento.Progreso:
                        progressBar1.Value = e.Porcentaje > progressBar1.Maximum ? progressBar1.Maximum : e.Porcentaje;
                        progressBar1.Refresh();
                        break;
                    case NetworkEventArgs.TipoEvento.Error:
                        break;
                    case NetworkEventArgs.TipoEvento.HostNameResuelto:
                        RefreshHostsListAsync();
                        break;
                    case NetworkEventArgs.TipoEvento.BusquedaCompletada:
                        Log("Busqueda completada, " +
                            Networks[comboBoxNICS.SelectedIndex].Hosts.Count +
                            " hosts encontrados");
                        progressBar1.Visible = false;
                        buttonDiscover.Enabled = true;
                        RefreshHostsListAsync();
                        break;
                    default:
                        break;
                }
            }
        }

        /// <summary>
        /// Delegado para realizar llamada a RefreshHostsListAsync desde otro proceso
        /// </summary>
        delegate void RefreshHostListCallback();

        /// <summary>
        /// Actualiza la tabla de hosts encontrados
        /// </summary>
        private void RefreshHostsListAsync()
        {
            if (listViewHosts.InvokeRequired)
            {
                RefreshHostListCallback d = new RefreshHostListCallback(RefreshHostsListAsync);
                Invoke(d);
            }
            else
            {
                listViewHosts.Items.Clear();
                listViewHosts.Refresh();
                foreach (Host item in Networks[comboBoxNICS.SelectedIndex].Hosts)
                {
                    // LLENAR CAMPOS:  ip,  hostname,  mac,  vendor, latencia, detalles
                    listViewHosts.Items.Add(new ListViewItem(
                                             new string[] {
                                    item.Addr.ToString(),
                                    item.Hostname,
                                    item.MAC.ToStringSeparated('-'),
                                    item.Vendor,
                                    item.Latencia.ToString() + " ms",
                                    item.Details}));
                }
                listViewHosts.Refresh();
            }
        }

        /// <summary>
        /// Escribe en el textbox el evento pasado por parametro y adjunta la hora.
        /// </summary>
        /// <param name="evento">texto para escribir en el log</param>
        public void Log(string evento)
        {
            textBoxLog.Text += "[" + DateTime.Now.ToString("HH:mm:ss.fff") + "] " + evento + Environment.NewLine;
            textBoxLog.SelectionStart = textBoxLog.Text.Length - 2;
            textBoxLog.ScrollToCaret();
            textBoxLog.Refresh();
        }

        private void copiarMACToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                Clipboard.SetText(Networks[comboBoxNICS.SelectedIndex].Hosts[listViewHosts.FocusedItem.Index].MAC.ToStringSeparated(':'));
                Log(Clipboard.GetText() + " copiado al portapapeles");
            }
            catch (Exception) { }
        }


        private void copiarIPToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                Clipboard.SetText(Networks[comboBoxNICS.SelectedIndex].Hosts[listViewHosts.FocusedItem.Index].Addr.ToString());
                Log(Clipboard.GetText() + " copiado al portapapeles");
            }
            catch (Exception) { }
        }

        private void copiarHostnameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            try
            {
                Clipboard.SetText(Networks[comboBoxNICS.SelectedIndex].Hosts[listViewHosts.FocusedItem.Index].Hostname);
                Log(Clipboard.GetText() + " copiado al portapapeles");
            }
            catch (Exception) { }
        }

        private void buttonActualizar_Click(object sender, EventArgs e)
        {
            ActualizarInterfaces();
        }

        void ActualizarInterfaces()
        {
            Log("Explorando interfaces...");
            Networks = Network.get_active_Networks();
            comboBoxNICS.Items.Clear();
            Log("se encontro " + Networks.Length + " interfaces activa/s");
            foreach (Network net in Networks)
            {
                net.HostsChanged += new NetworkEventHandler(HostsChangedAsync);
                comboBoxNICS.Items.Add(net.NIC.Name + " - " + net.NIC.Description);
            }
            if (comboBoxNICS.Items.Count == 0)
                buttonDiscover.Enabled = false;
            else
            {
                comboBoxNICS.SelectedIndex = 0;
                buttonDiscover.Enabled = true;
            }
        }

        private void radioButtonTodaLaRed_CheckedChanged(object sender, EventArgs e)
        {
            textBoxDesde.Enabled = radioButtonPersonalizar.Checked;
            textBoxHasta.Enabled = radioButtonPersonalizar.Checked;
            IpDesde = Networks[comboBoxNICS.SelectedIndex].SubnetAddr.Incrementar(1);
            IpHasta = Networks[comboBoxNICS.SelectedIndex].SubnetAddr.Incrementar(Networks[comboBoxNICS.SelectedIndex].maxHosts);
            textBoxDesde.Text = IpDesde.ToString();
            textBoxHasta.Text = IpHasta.ToString();
        }

        private void textBoxDesde_TextChanged(object sender, EventArgs e)
        {
            if (IPAddress.TryParse(textBoxDesde.Text, out IpDesde))
            {
                textBoxDesde.ForeColor = Color.Empty;
                Console.WriteLine(IpDesde.ToString());
                ActualizarDiferencia();
            }
            else textBoxDesde.ForeColor = Color.Red;

        }

        private void textBoxHasta_TextChanged(object sender, EventArgs e)
        {
            if (IPAddress.TryParse(textBoxHasta.Text, out IpHasta))
            {
                textBoxHasta.ForeColor = Color.Empty;
                ActualizarDiferencia();
            }
            else textBoxHasta.ForeColor = Color.Red;

        }

        private void ActualizarDiferencia()
        {
            if (IpDesde.CompareTo(IpHasta) == -1 || IpDesde.CompareTo(IpHasta) == 0)
            {
                Cantidad = IpHasta.diferenciaCon(IpDesde) + 1;
                labelCantidad.Text = Cantidad.ToString();
            }

        }

        private void acercaDeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new FormAcercaDe().Show();
        }
    }
}
